import { Observable } from 'rxjs/Observable';
import AbstractModel from '../model/abstract.model';
import { Response } from '@angular/http';
import { TokenStorageService } from './storage.service';

export interface ServerResponse { 
    code: number;
    msg: string;
    data: any;
}

export class CustomerError extends Error { 
    constructor(public code: number, public msg: string) { 
        super(msg);
    }
    toJSON() { 
        return {
            code: this.code,
            msg: this.msg,
        }
    }
}

export interface ClassInterface extends AbstractModel { 
    new(obj: any);
}

export default abstract class AbstractService<T extends AbstractModel> { 
    abstract $get(id, options?);
    abstract $find(where, options?);
    abstract $update(obj, options?);
    abstract $add(obj, options?);
    abstract $delete(id, options?);
    
    constructor(public $class: ClassInterface, public tokenStorage: TokenStorageService) { 
    }

    protected handleData(result: ServerResponse) { 
        if (result.code == 403 && result.msg == '登录已失效') { 
            this.tokenStorage.isValid = false;
            console.warn(result);
        }
        if (result.code != 0) { 
            throw new CustomerError(result.code, result.msg);
        }
        return result.data;
    }

    async get(id, options) :Promise<T>{ 
        let result = await this.$get(id, options)
            .toPromise();
        let data = this.handleData(result);
        let cls = this.$class;
        return new cls(data);
    }
    async find(where, options) :Promise<T[]>{ 
        let result = await this.$find(where, options)
            .toPromise();
        let arr = this.handleData(result);
        arr = arr.map((obj) => { 
            return new this.$class(obj);
        })
        // console.log("Arr==>", arr)
        return arr;
    }
    async update(obj, options) :Promise<T>{ 
        let result = await this.$update(obj, options)
            .toPromise();
        let data = this.handleData(result);
        return new this.$class(data);
    }
    async add(obj, options) :Promise<T>{ 
        let result = await this.$add(obj, options)
            .toPromise();
        let data = this.handleData(result);
        return new this.$class(data);
    }
    async delete(id, options) :Promise<boolean>{ 
        let result = await this.$delete(id, options)
            .toPromise();
        let data = this.handleData(result);
        return !!data;
    }
}